/*-------------------------------------------------------------------------
 *
 * pg_config.c
 *        Expose same output as pg_config except as an SRF
 *
 * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * IDENTIFICATION
 *      src/backend/utils/misc/pg_config.c
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include "funcapi.h"
#include "miscadmin.h"
#include "catalog/pg_type.h"
#include "common/config_info.h"
#include "utils/builtins.h"
#include "utils/elog.h"
#include "port.h"

Datum
pg_config(PG_FUNCTION_ARGS)
{
    ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
    Tuplestorestate *tupstore;
    HeapTuple    tuple;
    TupleDesc    tupdesc;
    AttInMetadata *attinmeta;
    MemoryContext per_query_ctx;
    MemoryContext oldcontext;
    ConfigData *configdata;
    size_t        configdata_len;
    char       *values[2];
    int            i = 0;

    /* check to see if caller supports us returning a tuplestore */
    if (!rsinfo || !(rsinfo->allowedModes & SFRM_Materialize))
        ereport(ERROR,
                (errcode(ERRCODE_SYNTAX_ERROR),
                 errmsg("materialize mode required, but it is not "
                        "allowed in this context")));

    per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
    oldcontext = MemoryContextSwitchTo(per_query_ctx);

    /* get the requested return tuple description */
    tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc);

    /*
     * Check to make sure we have a reasonable tuple descriptor
     */
    if (tupdesc->natts != 2 ||
        tupdesc->attrs[0]->atttypid != TEXTOID ||
        tupdesc->attrs[1]->atttypid != TEXTOID)
        ereport(ERROR,
                (errcode(ERRCODE_SYNTAX_ERROR),
                 errmsg("query-specified return tuple and "
                        "function return type are not compatible")));

    /* OK to use it */
    attinmeta = TupleDescGetAttInMetadata(tupdesc);

    /* let the caller know we're sending back a tuplestore */
    rsinfo->returnMode = SFRM_Materialize;

    /* initialize our tuplestore */
    tupstore = tuplestore_begin_heap(true, false, work_mem);

    configdata = get_configdata(my_exec_path, &configdata_len);
    for (i = 0; i < configdata_len; i++)
    {
        values[0] = configdata[i].name;
        values[1] = configdata[i].setting;

        tuple = BuildTupleFromCStrings(attinmeta, values);
        tuplestore_puttuple(tupstore, tuple);
    }

    /*
     * no longer need the tuple descriptor reference created by
     * TupleDescGetAttInMetadata()
     */
    ReleaseTupleDesc(tupdesc);

    tuplestore_donestoring(tupstore);
    rsinfo->setResult = tupstore;

    /*
     * SFRM_Materialize mode expects us to return a NULL Datum. The actual
     * tuples are in our tuplestore and passed back through rsinfo->setResult.
     * rsinfo->setDesc is set to the tuple description that we actually used
     * to build our tuples with, so the caller can verify we did what it was
     * expecting.
     */
    rsinfo->setDesc = tupdesc;
    MemoryContextSwitchTo(oldcontext);

    return (Datum) 0;
}
